/* Copyright 2009 Sony Mathew, Minneapolis MN. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at  http://www.apache.org/licenses/LICENSE-2.0  Unless required by applicable law or agreed to in writing, software  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */
package com.xsm.gwt.widgets.client;

import com.xsm.lite.event.Event;
import com.xsm.lite.util.StringUtil;

/**
 * Indicates events for viewing some content.
 * 
 * @author Sony Mathew
 */
public abstract class ViewEvent extends Event {

    protected static String SEP = "/";
    
    /**
     * The key is the class name without package.  All class names for view events should be unique.
     * 
     * author Sony Mathew
     */
    public static String getTokenKey(Class<? extends ViewEvent> viewEventClass) {
        String className = viewEventClass.getName();
        int lastDot = className.lastIndexOf(".");
        className = className.substring(lastDot+1);
        return className;
    }
    
    /**
     * @return boolean: true if the token belongs to the given viewEventClass, false otherwise.
     * 
     * Note: if modifiying this rule, also modify fromToken() which replicates the same rules.
     * 
     * author Sony Mathew
     */
    public static boolean isTokenOf(String token, Class<? extends ViewEvent> viewEventClass) {
        if (token != null && (token=token.trim()).length()>0) {
            String tokenKey = getTokenKey(viewEventClass);
            if (token.startsWith(tokenKey)) {
                return true;
            }
        }        
        return false;
    }
    
    /**
     * Indicate whether this ViewEvent is recorded into history.
     * By default, always true, but sometimes (e.g. certain views or popups) you may override to return false.
     * 
     * author Sony Mathew
     */
    public boolean isHistoryRecorded() {
        return true;
    }
    
    /**
     * Invoked to get all the properties of this event you want serialized in the History token.
     * Return null if no props (default) and no props will be serialized.
     * if a specific prop value is null use String value "null" instead of empty string.
     *  Always doing ""+myProp or String.valueOf(myProp) will give you "null" if your prop value is null.
     * 
     * author Sony Mathew
     */
    public String[] getTokenProps() {
        return null;
    }
    
    /**
     * Invoked to set all the properties there were previously serialized into a history token.
     * Will NOT be called if no props exist on token - so will never call with null.
     * 
     * author Sony Mathew
     */
    public void setTokenProps(String[] tokenProps) {        
    }
    
    
    /**
     * encode the given view event into an history token that can be recorded.
     * 
     * author Sony Mathew
     */
    public String toToken() {
        String token = getTokenKey(getClass());
        
        String[] props = getTokenProps();
        if (props != null && props.length > 0) {
            token += (SEP + concat(props) + SEP); 
        }
        
        return token;
    }
        
    /**
     * decode the given history token into a view event.
     * If the token is not of this event then will return false with no effect.
     * 
     * Note: if modifiying this rule, also modify isTokenOf() which replicates the same rules.
     * 
     * author Sony Mathew
     */
    public boolean fromToken(String token) {
        String origToken = token;
        if (token != null && (token=token.trim()).length()>0) {
            String tokenKey = getTokenKey(getClass());
            if (token.startsWith(tokenKey)) {
                if (token.length() > tokenKey.length()) {
                    token = token.substring(tokenKey.length());
                    if (token.startsWith(SEP)) {
                        token = token.substring(SEP.length());
                        if (token.length() > 0) {
                            String[] propsNew = split(token);
                            String[] propsCurr = getTokenProps();
                            if (propsCurr == null || propsCurr.length != propsNew.length) {
                                throw new IllegalStateException("Incompatible View token [" + origToken +"]");
                            }
                            setTokenProps(propsNew);
                        }
                    }
                }
                return true;
            }
        }        
        
        return false;
    }
        
    /**
     * Concats the given properties into a history token.
     * 
     * author Sony Mathew
     */
    public static String concat(String... props) {
        return StringUtil.concat(props, SEP);
    }
    
    /**
     * Splits the given history token into props originally concated.
     * 
     * author Sony Mathew
     */
    public static String[] split(String token) {
        if (SEP.length() != 1) {
            throw new IllegalStateException("Invalid Separator for View Events");
        }
        return StringUtil.strictSplit(token, SEP.charAt(0));
    }

    /**
     * If a token property is a String - use this utility to convert
     *  it back to String.  Will return null if prop value was "null" string value.
     *  
     * author Sony Mathew
     */
    public static String parseStringProp(String prop) {
        if (prop == null || prop.equals("null")) {
            return null;
        }
        return prop; 
    }
    
    /**
     * If a token property is an Integer - use this utility to convert
     *  it back to an integer.  Will return null if prop value was "null" string value.
     *  
     * author Sony Mathew
     */
    public static Integer parseIntegerProp(String prop) {
        prop = parseStringProp(prop);
        if (prop == null) {
            return null;
        }
        if ((prop=prop.trim()).length()==0) {
            return null;
        }
        
        try {
            Integer propInt = new Integer(Integer.parseInt(prop));
            return propInt;
        }catch(Exception x) {
            return null;
        }
    }

    /**
     * Cannot override, define getTokenProps()/setTokenProps() instead.
     * 
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public final boolean equals(Object o) {
        if (o == this) {
            return true;
        }
        if (o == null || !(o.getClass().equals(this.getClass()))) {
            return false;
        }
        ViewEvent other = (ViewEvent)o;
        return this.toToken().equals(other.toToken());        
    }

    /**
     * Cannot override, define getTokenProps()/setTokenProps() instead.
     * 
     * @see java.lang.Object#hashCode()
     */
    @Override
    public final int hashCode() {
        return toToken().hashCode();
    }

    @Override
    public String toString() {
        return toToken();
    }
    
        
}
